package org.yxj.picturerecognitionlibrary;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;

import com.google.gson.Gson;

/**
 * 图片识别效果
 *
 * @author yxJ on 2024/1/29
 */
public class PictureRecognitionView extends View {

    /**
     * 原图
     */
    private Bitmap bitmapSrc;

    /**
     * 识别的图片
     */
    private Bitmap bitmap;

    /**
     * 图片的大小
     */
    private int[] bitmapSize = new int[2];

    /**
     * 画纸
     */
    private Canvas mCanvas;
    private Bitmap bitmapBg;


    /**
     * 画笔的宽度
     */
    private int strokeWidth = 3;

    /**
     * 四个角画笔的宽度
     */
    private int strokeRectWidth = strokeWidth * 4;

    /**
     * 框选的区域
     */
    private Rect rect = new Rect();

    /**
     * 框选区域偏移量
     * x 方向
     * y 方向
     */
    private PointF pointF = new PointF();


    public PictureRecognitionView(Context context) {
        this(context, null);
    }


    public PictureRecognitionView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }


    public PictureRecognitionView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }


    /**
     * 初始化显示
     *
     * @param bitmap
     */
    public void init(Bitmap bitmap) {
        this.bitmapSrc = bitmap;
        initBitmap(bitmap);
        initCanvas();
    }


    /**
     * 初始显示图片的大小
     *
     * @param bitmap
     */
    private void initBitmap(Bitmap bitmap) {
        if (bitmap != null) {
            Log.v("[initCanvas]->", "bw = " + bitmap.getWidth());
            Log.v("[initCanvas]->", "bh = " + bitmap.getHeight());

            int w = getResources().getDisplayMetrics().widthPixels;
            int h = getResources().getDisplayMetrics().heightPixels;

            int bw = bitmap.getWidth();
            int bh = bitmap.getHeight();

            float scale;
            float scale1 = 0;
            float scale2 = 0;

            //宽度的缩放比
            scale1 = (float) (w * 1.0 / bw);
            //高度的缩放比
            scale2 = (float) (h * 1.0 / bh);

            /**
             * 获得缩放比
             */
            scale = Math.min(scale1, scale2);
            Bitmap b = null;
            if (bw > w || bh > h) {
                Matrix matrix = new Matrix();
                matrix.postScale(scale, scale);
                b = Bitmap.createBitmap(bitmap, 0, 0, bw, bh, matrix, false);
                Log.v("[initCanvas]->", "scale = " + scale);
            } else {
                b = bitmap;
            }

            bitmapSize[0] = b.getWidth();
            bitmapSize[1] = b.getHeight();
            Log.v("[bitmapSize]->", "bitmapSize = " + new Gson().toJson(bitmapSize));
            this.bitmap = b;
            this.coordinateBean = getCoordinateBean();


            setMeasuredDimension(bitmapSize[0], bitmapSize[1]);


            ViewGroup.LayoutParams p = getLayoutParams();
            p.width = bitmapSize[0];
            p.height = bitmapSize[1];
            setLayoutParams(p);
        }
    }


    /**
     * 创建一张绘制的纸
     */
    private void initCanvas() {
        bitmapBg = Bitmap.createBitmap(bitmapSize[0], bitmapSize[1], Bitmap.Config.ARGB_8888);
        mCanvas = new Canvas(bitmapBg);
        if (bitmap != null) {
            mCanvas.drawBitmap(bitmap, 0, 0, new Paint());
        }


        if (coordinateBean != null) {
            rect.set(strokeRectWidth + coordinateBean.left,
                    strokeRectWidth + coordinateBean.top,
                    coordinateBean.right - strokeRectWidth,
                    coordinateBean.bottom - strokeRectWidth);
            drawRect();
        }
    }


    /**
     * 初始化传入的坐标
     */
    private float[] coordinate = new float[4];

    /**
     * 区域框数据
     */
    public PictureRecognitionView setCoordinateBean(float l, float t, float r, float b) {
        coordinate[0] = l;
        coordinate[1] = t;
        coordinate[2] = r;
        coordinate[3] = b;
        return this;
    }


    private CoordinateBean getCoordinateBean() {
        float x1 = coordinate[0] * 1.0f / bitmapSrc.getWidth() * bitmapSize[0];
        float x2 = (coordinate[2] * 1.0f / bitmapSrc.getWidth() * bitmapSize[0]);
        float y1 = (coordinate[1] * 1.0f / bitmapSrc.getHeight() * bitmapSize[1]);
        float y2 = (coordinate[3] * 1.0f / bitmapSrc.getHeight() * bitmapSize[1]);
        Log.v("[getCoordinateBean]->", new StringBuilder()
                .append("x1=" + x1).append("    ")
                .append("x2=" + x2).append("    ")
                .append("y1=" + y1).append("    ")
                .append("y2=" + y2).append("    ").toString());

        PictureRecognitionView.CoordinateBean coordinateBean = new PictureRecognitionView.CoordinateBean();
        coordinateBean.left = (int) x1;
        coordinateBean.top = (int) y1;
        coordinateBean.right = (int) x2;
        coordinateBean.bottom = (int) y2;
        return coordinateBean;
    }


    /**
     * 重置区域框数据
     */
    public PictureRecognitionView upDataCoordinateBean(int l, int t, int r, int b) {
        coordinate[0] = l;
        coordinate[1] = t;
        coordinate[2] = r;
        coordinate[3] = b;
        this.coordinateBean = getCoordinateBean();
        if (coordinateBean != null) {

            /**
             * 初始化框选区域
             */
            rect.set(strokeRectWidth + coordinateBean.left,
                    strokeRectWidth + coordinateBean.top,
                    coordinateBean.right - strokeRectWidth,
                    coordinateBean.bottom - strokeRectWidth);

            /**
             * 绘制底图
             */
            if (bitmap != null) {
                clear();
                mCanvas.drawBitmap(bitmap, 0, 0, new Paint());
            }

            /**
             * 重置
             */
            reset();
            /**
             * 绘制选中框
             */
            drawRect();
        }
        return this;
    }


    /**
     * 重置
     */
    private void reset() {
        lastChangeSize[0] = lastChangeSize[1] = lastChangeSize[2] = lastChangeSize[3] = 0;
        currentMove[0] = currentMove[1] = currentMove[2] = currentMove[3] = 0;
//        canChange[0] = canChange[1] = canChange[2] = canChange[3] = false;

        isCanChangeRect = false;
        isCanDragRect = false;
        moveY = 0;
        moveX = 0;
        pointF.x = pointF.y = 0;
    }


    /**
     * 显示坐标
     */
    private CoordinateBean coordinateBean;

    /**
     * 坐标区域
     */
    public static class CoordinateBean {
        public int left;
        public int top;
        public int right;
        public int bottom;
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(bitmapSize[0], bitmapSize[1]);
    }


    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
        if (bitmapBg != null) {
            canvas.save();
            canvas.drawBitmap(bitmapBg, 0, 0, new Paint());
            canvas.restore();
        }
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {

        /**
         * 坐标数据不能为空
         */
        if (coordinateBean == null)
            return false;

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downX = event.getX();
                downY = event.getY();

                /**
                 *检测是否可以编辑放大
                 */
                isCanChangeRect = checkCanChangeRect();
                /**
                 * 检测是否可以拖拽框选
                 */
                isCanDragRect = checkDragRect(event.getX(), event.getY());

                break;

            case MotionEvent.ACTION_MOVE:
                if (isCanChangeRect) {
                    /**
                     * 编辑放大或者缩小
                     */
                    dragChangeRect(event);
                } else {
                    /**
                     * 移动框选区域
                     */
                    dragRect(event);
                }
                break;


            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_OUTSIDE:
                tempX = 0;
                tempY = 0;
                if (isCanChangeRect) {
                    stopLastChange();
                } else {
                    stopLaseDragRect();
                }
                break;
        }
        return true;
    }


    /**************************************************编辑识别框****************************************************/
    /**
     * 当前四个边的变化大小
     */
    private float[] currentMove = new float[4];
    /**
     * 记录上一次改变的大小
     */
    private float[] lastChangeSize = new float[4];
    /**
     * 四边是否可改变大小
     */
    private boolean[] canChange = new boolean[4];
    /**
     * 触摸间隔
     */
    private int canChangeSeep = 30;
    /**
     * 检测是否可以改变大小
     */
    private boolean isCanChangeRect = false;

    /**
     * 拖拽改变框选大小
     */
    private void dragChangeRect(MotionEvent event) {
        //左
        if (canChange[0]) {
            currentMove[0] = event.getX() - downX + lastChangeSize[0];
        }
        //上
        if (canChange[1]) {
            currentMove[1] = event.getY() - downY + lastChangeSize[1];
        }
        //右
        if (canChange[2]) {
            currentMove[2] = event.getX() - downX + lastChangeSize[2];
        }
        //下
        if (canChange[3]) {
            currentMove[3] = event.getY() - downY + lastChangeSize[3];
        }

        Log.v("[dragChangeRect]->", "可变的方向 canChange = " + new Gson().toJson(canChange));
        Log.v("[dragChangeRect]->", "变化的值 currentMove = " + new Gson().toJson(currentMove));

        /**
         * 实时检测框选区域大小
         */
        rectSize(currentMove);

        if (bitmap != null) {
            clear();
            mCanvas.drawBitmap(bitmap, 0, 0, new Paint());
        }

        drawRect();
    }


    /**
     * 框选区域大小
     */
    private void rectSize(float[] f) {

//        if (rect.width()<minSize || rect.height()<minSize)return;


        Rect r = new Rect();
        r.left = (int) (coordinateBean.left + moveX + f[0] + strokeRectWidth);
        if (r.left < strokeRectWidth) {
            r.left = strokeRectWidth;
        }

        r.top = (int) (coordinateBean.top + moveY + f[1] + strokeRectWidth);
        if (r.top < strokeRectWidth) {
            r.top = strokeRectWidth;
        }


        r.right = (int) (coordinateBean.right + moveX + f[2] - strokeRectWidth);
        /*右边界*/
        if (r.right >= bitmapSize[0] - strokeRectWidth) {
            r.right = bitmapSize[0] - strokeRectWidth;
        }


        r.bottom = (int) (coordinateBean.bottom + moveY + f[3] - strokeRectWidth);
        /*下边界*/
        if (r.bottom >= bitmapSize[1] - strokeRectWidth) {
            r.bottom = bitmapSize[1] - strokeRectWidth;
        }

        Log.v("[rectSize]->", " rect = " + new Gson().toJson(r));
        Log.v("[rectSize]->", " r.bottom = " + r.bottom + ", bitmapSize[1] - strokeRectWidth = " + (bitmapSize[1] - strokeRectWidth));
        Log.v("[rectSize]->", " bitmapSize  = " + new Gson().toJson(bitmapSize));
        /**
         * 检测边界
         */
        checkBoundary(r, f);
        rect.set(r);


        /*回调选中的*/
        callBack();
    }


    /**
     * 回调选中的
     */
    private void callBack() {
        try {
            if (checkRectListener != null) {
                if (rect.left >= 0 && rect.top >= 0) {

                    int x = rect.left;
                    int y = rect.top;

                    int w = rect.width();
                    int h = rect.height();

                    if ((x + w) > bitmapSize[0]) {
                        w = bitmapBg.getWidth() - x;
                    }
                    if ((y + h) > bitmapSize[1]) {
                        h = bitmapBg.getHeight() - y;
                    }

                    Bitmap b = Bitmap.createBitmap(bitmapBg, x, y, w, h);
                    checkRectListener.onResult(b);
                }
            }
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        }
    }


    /**
     * 记录上一次编辑的大小
     */
    private void stopLastChange() {
        lastChangeSize[0] = currentMove[0];
        lastChangeSize[1] = currentMove[1];
        lastChangeSize[2] = currentMove[2];
        lastChangeSize[3] = currentMove[3];
    }


    /**
     * 检测是否可以改变大小
     */
    private boolean checkCanChangeRect() {
        /**
         * 重置数组
         */
        canChange[0] = false;
        canChange[1] = false;
        canChange[2] = false;
        canChange[3] = false;


        boolean LT_x1 = downX > (rect.left - canChangeSeep);
        boolean LT_x2 = downX < (rect.left + canChangeSeep);
        boolean LT_y1 = downY > (rect.top - canChangeSeep);
        boolean LT_y2 = downY < (rect.top + canChangeSeep);
        if (LT_x1 && LT_x2 && LT_y1 && LT_y2) {
            Log.v("[checkCanChangeRect]->", "左上角");
            canChange[0] = true;
            canChange[1] = true;
            return true;
        }


        boolean RT_x1 = downX > (rect.right - canChangeSeep);
        boolean RT_x2 = downX < (rect.right + canChangeSeep);
        boolean RT_y1 = downY > (rect.top - canChangeSeep);
        boolean RT_y2 = downY < (rect.top + canChangeSeep);
        if (RT_x1 && RT_x2 && RT_y1 && RT_y2) {
            Log.v("[checkCanChangeRect]->", "右上角");
            canChange[1] = true;
            canChange[2] = true;
            return true;
        }



        boolean RB_x1 = downX > (rect.right - canChangeSeep);
        boolean RB_x2 = downX < (rect.right + canChangeSeep);
        boolean RB_y1 = downY > (rect.bottom - canChangeSeep);
        boolean RB_y2 = downY < (rect.bottom + canChangeSeep);
        if (RB_x1 && RB_x2 && RB_y1 && RB_y2) {
            Log.v("[checkCanChangeRect]->", "左下角");
            canChange[2] = true;
            canChange[3] = true;
            return true;
        }


        boolean LB_x1 = downX > (rect.left - canChangeSeep);
        boolean LB_x2 = downX < (rect.left + canChangeSeep);
        boolean LB_y1 = downY > (rect.bottom - canChangeSeep);
        boolean LB_y2 = downY < (rect.bottom + canChangeSeep);
        if (LB_x1 && LB_x2 && LB_y1 && LB_y2) {
            Log.v("[checkCanChangeRect]->", "左下角");
            canChange[3] = true;
            canChange[0] = true;
            return true;
        }




        //左边
        if (downX < (rect.left + canChangeSeep)
                && downX > (rect.left - canChangeSeep)
                && downY < rect.bottom
                && downY > rect.top) {
            Log.v("[checkCanChangeRect]->", "左边");
            canChange[0] = true;
            return true;
        }


        //上
        if (downY < (rect.top + canChangeSeep)
                && downY > (rect.top - canChangeSeep)
                && downX < rect.right
                && downX > rect.left) {
            Log.v("[checkCanChangeRect]->", "上边");
            canChange[1] = true;
            return true;
        }


        //右边
        if (downX < (rect.right + canChangeSeep)
                && downX > (rect.right - canChangeSeep)
                && downY < rect.bottom
                && downY > rect.top) {
            Log.v("[checkCanChangeRect]->", "右边");
            canChange[2] = true;
            return true;
        }


        //下
        if (downY < (rect.bottom + canChangeSeep)
                && downY > (rect.bottom - canChangeSeep)
                && downX < rect.right
                && downX > rect.left) {
            Log.v("[checkCanChangeRect]->", "下边");
            canChange[3] = true;
            return true;
        }


        return false;
    }


    /**************************************************绘制识别框****************************************************/
    /**
     * 绘制识别框
     */
    public void drawRect() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            mCanvas.saveLayer(new RectF(0, 0, bitmapSize[0], bitmapSize[1]), null);
        }

        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(MaskColor);
        mCanvas.drawRect(new RectF(0, 0, bitmapSize[0], bitmapSize[1]), paint);
        /**
         * 绘制边线
         */
        paint.setStrokeWidth(strokeWidth);
        paint.setStrokeJoin(Paint.Join.ROUND);
        paint.setStrokeCap(Paint.Cap.ROUND);
        paint.setStyle(Paint.Style.STROKE);
        paint.setColor(LineColor);
        mCanvas.drawRect(rect, paint);
        /**
         * 绘制四个角
         */
        drawCorner();

        /**
         * 1、PorterDuffXfermode
         */
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));

        /**
         * 溶解目标区域
         */
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.TRANSPARENT);
        mCanvas.drawRect(rect, paint);

        /**
         * 合并图层
         */
        mCanvas.restore();

        /**
         * 刷新画布
         */
        postInvalidate();
    }


    /***
     * 四个角的颜色
     */
    private int CornerColor = Color.WHITE;
    /**
     * 边线颜色
     */
    private int LineColor = Color.YELLOW;
    /**
     * 蒙层的颜色
     */
    private int MaskColor = Color.parseColor("#50000000");


    public PictureRecognitionView setMaskColor(int maskColor) {
        MaskColor = maskColor;
        return this;
    }

    public PictureRecognitionView setLineColor(int lineColor) {
        LineColor = lineColor;
        return this;
    }

    public PictureRecognitionView setCornerColor(int cornerColor) {
        CornerColor = cornerColor;
        return this;
    }


    /**
     * 绘制四个角
     */
    private void drawCorner() {
        int seep = 40;

        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setStyle(Paint.Style.STROKE);
        paint.setColor(CornerColor);
        paint.setStrokeWidth(strokeRectWidth);

        //绘制左上角
        Path leftTopPath = new Path();
        leftTopPath.moveTo(rect.left + seep, rect.top);
        leftTopPath.lineTo(rect.left, rect.top);
        leftTopPath.lineTo(rect.left, rect.top + seep);
        mCanvas.drawPath(leftTopPath, paint);

        //绘制右上角
        Path rightTopPath = new Path();
        rightTopPath.moveTo(rect.right - seep, rect.top);
        rightTopPath.lineTo(rect.right, rect.top);
        rightTopPath.lineTo(rect.right, rect.top + seep);
        mCanvas.drawPath(rightTopPath, paint);

        //绘制右下角
        Path rightBottomPath = new Path();
        rightBottomPath.moveTo(rect.right, rect.bottom - seep);
        rightBottomPath.lineTo(rect.right, rect.bottom);
        rightBottomPath.lineTo(rect.right - seep, rect.bottom);
        mCanvas.drawPath(rightBottomPath, paint);

        //绘制左下角
        Path leftBottomPath = new Path();
        leftBottomPath.moveTo(rect.left, rect.bottom - seep);
        leftBottomPath.lineTo(rect.left, rect.bottom);
        leftBottomPath.lineTo(rect.left + seep, rect.bottom);
        mCanvas.drawPath(leftBottomPath, paint);
    }


    /**************************************************拖拽识别框****************************************************/
    private boolean isCanDragRect = false;
    //按下的坐标
    private float downX = 0;
    private float downY = 0;

    //当前框选区域移动的距离
    private float moveX = 0;
    private float moveY = 0;

    /**
     * 拖拽选中区域
     *
     * @param event
     */
    private void dragRect(MotionEvent event) {
        if (coordinateBean == null) return;
        if (isCanDragRect) {

            if (!isBoundaryX(event)) {
                moveX = event.getX() - downX + pointF.x;
            }

            if (!isBoundaryY(event)) {
                moveY = event.getY() - downY + pointF.y;
            }

            if (bitmap != null) {
                clear();
                mCanvas.drawBitmap(bitmap, 0, 0, new Paint());
            }

            /**
             * 默认 区域的大小
             */
            rectSize(lastChangeSize);
            //绘制选中框
            drawRect();
        }
    }


    float tempX;
    float tempY;
    int minSize = canChangeSeep * 3;

    /**
     * 检测边界
     */
    private void checkBoundary(Rect r, float[] f) {
        /**
         * 编辑
         * 控制最小
         */
        if (r.width() <= minSize) {
            r.right = r.left + minSize;
        }

        if (r.height() <= minSize) {
            r.bottom = r.top + minSize;
        }

        Log.v("[checkBoundary]->", " r  = " + new Gson().toJson(r));
        Log.v("[checkBoundary]->", " bitmapSize  = " + new Gson().toJson(bitmapSize));
    }

    /**
     * 检测是否到编辑了
     *
     * @return
     */
    private boolean isBoundaryX(MotionEvent event) {
        boolean b = false;
        /*左边界*/
        if (rect.left <= strokeRectWidth && (event.getX() - downX < 0)) {
            b = true;
        }

        /*右边界*/
        if (rect.right >= bitmapSize[0] - strokeRectWidth && (event.getX() - downX > 0)) {
            b = true;
        }
        return b;
    }


    /**
     * @param event
     * @return
     */
    private boolean isBoundaryY(MotionEvent event) {
        boolean b = false;
        /*上边界*/
        if (rect.top <= strokeRectWidth && (event.getY() - downY < 0)) {
            b = true;
        }
        /*下边界*/
        if (rect.bottom >= bitmapSize[1] - strokeRectWidth && (event.getY() - downY > 0)) {
            b = true;
        }
        return b;
    }


    /**
     * 停止拖拽
     */
    private void stopLaseDragRect() {
        isCanDragRect = false;
        pointF.x = moveX;
        pointF.y = moveY;
    }


    /**
     * 检测拖拽
     *
     * @return
     */
    private boolean checkDragRect(float downX, float downY) {
        boolean isH = (rect.left < downX && downX < rect.right);
        boolean isV = (rect.top < downY && downY < rect.bottom);
        return isH && isV;
    }


    /**
     * 清除画笔
     */
    public void clear() {
        Paint paint = new Paint();
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        mCanvas.drawPaint(paint);
    }

    private CheckRectListener checkRectListener;


    /**
     * @param checkRectListener
     * @return
     */
    public PictureRecognitionView setCheckRectListener(CheckRectListener checkRectListener) {
        this.checkRectListener = checkRectListener;
        return this;
    }


    public interface CheckRectListener {
        /**
         * 选中区域 bitmap
         *
         * @param bitmap
         */
        void onResult(Bitmap bitmap);
    }
}
